Ordinary
About

TCP UDP

profileordilov / 2022. 3. 30

전송 계층

TCP와 UDP는 OSI 7계층이나 TCP/IP 모델 모두에서 전송 계층에 해당합니다. 전송 계층의 필요성을 알기 위해 간단하게 하위 계층에서 패킷을 보내서 받을 때를 보겠습니다.

  • 라우터의 라우팅 기능을 통해 요청한 네트워크 망에 아날로그 신호를 전송합니다.
  • 네트워크 망 내부에서 IP 주소를 이용해 스위치에서 MAC 주소를 찾습니다.
  • MAC 주소에 도착한 아날로그 신호를 랜 카드에서 데이터 신호로 변환합니다.

다른 네트워크 → 라우터 → 네트워크 망 → 스위치 → 컴퓨터

이런 과정을 거쳤을 때 프로그램에서 데이터를 받으려면 해결해야 하는 부분이 남았습니다.

  1. 어느 프로그램에서 받은 데이터를 사용할 건가?
  2. 데이터가 순서대로 전송되지 않으면 어떻게 할건가?
  3. 데이터가 라우터나 스위치 등을 거치면서 손상되거나 유실되면 어떻게 할까?

먼저 어느 프로그램에서 받은 데이터를 사용할 지를 해결합니다.

포트

같은 PC 내에서 여러 프로그램을 사용 중일 때 네트워크로 데이터를 받는다면 구분이 필요합니다. 이때 구분을 위해 사용하는 번호가 포트입니다.

포트는 16비트로 0~65535 숫자까지 가능합니다. 이 중에서 0 ~ 1023 까지를 well-known ports 라고 하고 서버에서 주로 사용합니다. 나머지 번호는 클라이언트가 송신할 때 주로 사용합니다. 웹 브라우저에서 요청하는 경우 포트가 자동으로 할당됩니다.

예를 들어 구글에 Postman으로 요청하면 송신에는 65478 포트를, 수신에는 443 포트가 사용된 걸 볼 수 있습니다.

전송 계층에서는 응용 계층에서 넘어온 데이터에 TCP/UDP 헤더를 합쳐서 다음 계층에 넘깁니다. 이렇게 데이터와 TCP/UDP 헤더를 합친 것을 세그먼트라고 합니다. 이때 TCP/UDP 헤더에는 송신자와 수신자의 포트가 들어갑니다. 포트를 이용하면서 어느 프로그램으로 전달할지 확인할 수 있습니다. 여기까지만 해도 전송 계층의 중요한 목적 중 하나인 어느 프로그램으로 보낼지를 해결했습니다. 다른 기능들은 추가하려면 신뢰성과 효율성 사이에서 트레이드 오프가 필요합니다. 그 중에서 신뢰성을 우선시하는 TCP(Transmission Control Protocol) 기준으로 설명하겠습니다.

재전송 제어

데이터를 한 번에 보낼 수 있으면 좋겠지만 보낼 수 있는 데이터의 크기는 정해져있습니다. 데이터 크기를 크게 보내면 대역폭을 많이 잡아먹게 되고, 데이터가 유실되거나 하는 경우 처리가 힘들어집니다. 데이터를 쪼개서 보내는 단위를 패킷이라고 합니다. 패킷의 최대 크기는 65,535로 실제 데이터 크기는 TCP 헤더 크기를 제외한 값이 됩니다.

이렇게 쪼개서 보낼 때 네트워크 환경에 따라 데이터의 순서가 다르게 전송될 수 있습니다. 다음 데이터가 먼저 오거나 하는 식으로 말이죠. 이런 조각난 데이터들을 모아서 원본 데이터로 만드려면 순서가 필요합니다. 그리고 순서대로 모았을 때 오지 않거나 손상된 데이터는 다시 보내줄 수 있어야 합니다.

이런 순서를 일련번호를 통해 전송하고, 어디까지 받았는지 응답을 확인 응답 번호에 담아서 전송합니다. 응답 번호로 받지 못한 값이 있다면 일정 시간 후에 다시 재전송해줍니다. 이렇게 제대로 응답받지 못한 것을 다시 받을 수 있게 보장해주는 것을 재전송 제어라고 합니다.

어디로 보낼지 정했고, 데이터를 쪼개서 어떤 순서로 보낼지 결정했습니다. 이제 어떤 식으로 전송할지 알아보겠습니다.

3 Way HandShaking

안정적으로 데이터를 보내려면 먼저 서버가 정상적으로 동작하는지 확인해야 합니다. 제대로 동작하는지 확인하기 위해 TCP는 연결을 합니다. 연결은 단순히 개념적인 것으로 데이터를 보내줄 수 있는지 확인하고 응답을 받는 것입니다. 확인과 응답에 대한 정보는 TCP 헤더 중 코드 비트에 포함됩니다.

코드 비트는 6비트로 구성되어 있는데 연결을 위해서 ACKSYN이 사용됩니다.

  1. 클라이언트가 연결 요청을 위해 SYN에 1을 담아 보냅니다.
  2. 서버가 연결 확립이 가능하면 ACK, SYN에 1을 담아 보냅니다.
  3. 클라이언트가 연결 확립 응답을 받았다는 응답을 ACK에 1을 담아 보냅니다.

4 Way HandShaking

데이터를 끝까지 다 받았는지 확인하기 위해서 연결 종료할 때도 요청을 주고 받습니다.

  1. 클라이언트가 연결 종료을 위해 FIN에 1을 담아 보냅니다.
  2. 서버가 연결 종료 응답으로 ACK에 1을 담아 보냅니다.
  3. 서버가 연결 종료 요청으로 FIN에 1을 담아 보냅니다.
  4. 클라이언트가 연결 종료 응답으로 ACK에 1을 담아 보냅니다.

클라이언트가 종료를 보내는 경우는 더 이상 요청할 데이터가 없을 때입니다. 서버가 종료를 보내는 경우는 요청한 데이터를 모두 전송했을 때입니다. 이때 3번에서 서버가 FIN을 보내고 클라이언트가 ACK를 보냈다고 해도 일정 시간동안 연결을 유지합니다. 이유는 FIN 이전에 보냈던 데이터들이 다 가지 않았는데 연결을 바로 종료하면 데이터를 다시 받을 수 없습니다. 그래서 FIN을 받은 이후에도 기간을 두고 데이터를 받고 완료하면 연결을 끊습니다.

이렇게 연결을 받고 끊는 것까지 가능하면 어느 정도 속도로 데이터를 보내줘야 할까요?

흐름 제어

데이터를 보낼 수 있는 만큼 빠르게 보내주면 좋아보이지만, 처리 속도가 따라주지 않으면 문제가 됩니다. 받을 수 있는지 확인하는 방법으로는 데이터를 받았다는 확인 응답 번호가 오면 다음 일련 번호를 보내는 것입니다. 이런 방식을 Stop and Wait 방식이라고 합니다.

하지만 이런 식으로 진행하면 대답이 올 때까지 기다렸다가 다시 보내주는 식으로 속도가 굉장히 느립니다. 이를 해결하기 위해 응답이 오지 않아도 어느 크기 정도는 함께 보냅니다. 이렇게 보낸 값들은 앞선 데이터들이 처리되는 동안 버퍼 라는 공간에 저장됩니다. 버퍼에서 임시적으로 값을 저장할 수 있지만 크기가 정해져있기 때문에 이보다 적은 크기로 받아야 합니다. 이 크기를 TCP 헤더에서 윈도우 크기에 담아 보내주며 3 Way Handshaking 중에 함께 보내서 크기를 공유합니다.

UDP (User Datagram Protocol)

UDP는 TCP와 달리 헤더에 안전성보다 간단하게 구성됩니다. 그만큼 신뢰성은 떨어지지만 오버헤드가 적습니다. 순서가 보장될 필요가 없거나, 데이터가 손실되도 재전송할 필요가 없을 때 사용됩니다. 데이터의 전송 단위도 TCP와 달리 데이터그램이라는 단위로 전송됩니다. 크기는 똑같이 최대 65535 바이트로, 크기가 넘어가면 잘라서 보냅니다.